/*
Xern Entity Relocation Network
Copyright (C) 2011 Ronie Salgado <roniesalg@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include "MapProcessor.hpp"

// Use a different epsilon here, don't reduce too much the main precision.
namespace MapXern
{
	const float BrushEpsilon = 0.001f;

	inline bool BrushNil(float value)
	{
		return -BrushEpsilon <= value && value <= BrushEpsilon;
	}

    void NormalizeBrush(boost::shared_ptr<Brush> brush)
    {
        size_t numfaces = brush->faces.size();
        for(size_t i = 0; i < numfaces; i++)
        {
            BrushFace &f1 = brush->faces[i];
            Plane &p1 = f1.plane;

            for(size_t j = 0; j < numfaces; j++)
            {
                BrushFace &f2 = brush->faces[j];
                Plane &p2 = f2.plane;

                if(i == j)
                    continue;

                for(size_t k = 0; k < numfaces; k++)
                {
                    BrushFace &f3 = brush->faces[k];
                    Plane &p3 = f3.plane;

                    if(i == k || j == k)
                        continue;

                    float den = p1.GetNormal().Dot(p2.GetNormal().Cross(p3.GetNormal()));
                    if(BrushNil(den))
                        continue;

                    Vector3 point = -p1.d*(p2.GetNormal().Cross(p3.GetNormal())) - p2.d*(p3.GetNormal().Cross(p1.GetNormal()))
                                -p3.d*(p1.GetNormal().Cross(p2.GetNormal()));
                    point /= den;

                    // Make sure that the point is part of the three faces.
                    if(!BrushNil(p1.GetDistance(point)))
                    	continue;

                    if(!BrushNil(p2.GetDistance(point)))
                    	continue;

                    if(!BrushNil(p3.GetDistance(point)))
						continue;

                    // Add the point to each face.
                    f1.AddVertex(point);
                    f2.AddVertex(point);
                    f3.AddVertex(point);
                }
            }
        }

        // Calculate the center point.
        Vector3 center(0.0f);
        size_t count = 0;
        for(size_t i = 0; i < numfaces; i++)
        {
            BrushFace &f = brush->faces[i];

            for(size_t j = 0; j < f.vertices.size(); j++)
            {
                center += f.vertices[j];
                count++;
            }
        }

        center /= count;
        brush->center = center;

        // Make sure that faces normals are pointing outside.
        for(size_t i = 0; i < numfaces; i++)
        {
            BrushFace &f = brush->faces[i];
            Plane &p = f.plane;
            float dist = p.a*center.x + p.b*center.y + p.c*center.z + p.d;
            if(dist > BrushEpsilon)
            {
                p.a = -p.a;
                p.b = -p.b;
                p.c = -p.c;
                p.d = -p.d;
            }
        }
    }

	void SortFaceVertices(BrushFace &face)
	{
		Vector3 center(0);
		int i, j;
		int smallest;
		float smallest_angle, angle;
		Plane pl;

		std::vector<Vector3> &vertices = face.vertices;
		if (vertices.size() < 3)
			return;

		// Calculate the center
		std::vector<Vector3>::iterator it = vertices.begin();
		for (; it != vertices.end(); it++)
			center += *it;
		center /= vertices.size();

		// Sort the vertices
		Vector3 normal = face.plane.GetNormal();
		for (i = 0; i < vertices.size() - 2; i++)
		{
			Vector3 a = (center - vertices[i]).Normalize();
			pl = Plane(vertices[i], center, center + normal);

			smallest_angle = -1.0f;
			smallest = i + 1;
			for (j = i + 1; j < vertices.size(); j++)
			{
				if (pl.GetDistance(vertices[j]) > BrushEpsilon)
				{
					Vector3 b = (center - vertices[j]).Normalize();
					angle = a.Dot(b);
					if (angle > smallest_angle)
					{
						smallest_angle = angle;
						smallest = j;
					}
				}
			}

			Vector3 t = vertices[i + 1];
			vertices[i + 1] = vertices[smallest];
			vertices[smallest] = t;
		}
	}

	// Texture mapping axis.
	struct TextureAxis
	{
		TextureAxis(const Vector3 &n, const Vector3 &u, const Vector3 &v)
			: n(n), u(u), v(v) {}

		Vector3 n;
		Vector3 u;
		Vector3 v;
	};

	static TextureAxis TextureAxisArray[] =
	{
	  TextureAxis(Vector3(0,0,1),  Vector3(1,0,0), Vector3(0,-1,0)), // north wall
	  TextureAxis(Vector3(0,0,-1), Vector3(-1,0,0), Vector3(0,-1,0)), // south wall
	  TextureAxis(Vector3(1,0,0),  Vector3(0,0,-1), Vector3(0,-1,0)), // west wall
	  TextureAxis(Vector3(-1,0,0), Vector3(0,0,1), Vector3(0,-1,0)), // east wall
	  TextureAxis(Vector3(0,1,0),  Vector3(-1,0,0), Vector3(0,0,-1)), // floor
	  TextureAxis(Vector3(0,-1,0), Vector3(-1,0,0), Vector3(0,0,1)), // ceiling
	};

	// Utility function to get the best texture axis.
	// Used for texture coordinate generation and light mapping.
	const TextureAxis &GetBestTextureAxis(const Vector3 &normal)
	{
		float best = 0;
		int bestAxis = 0;

		for(int i = 0; i < 6; i++)
		{
			float dot = normal.Dot(TextureAxisArray[i].n);
			if(dot > best)
			{
				best = dot;
				bestAxis = i;
			}
		}

		return TextureAxisArray[bestAxis];
	}

	// Generate texture coordinates.
	void GenerateTexCoords(BrushFace &face)
	{
		// Get the texture axis.
		const TextureAxis axis = GetBestTextureAxis(face.plane.GetNormal());

		// Calculate the texture angle.
		float ang = face.mapRot/180.0f * X_PI;
		float cosv = cos(ang);
		float sinv = sin(ang);

		// Calculate the texture scale.
		float sscale = 1.0f/face.mapScale.x;
		float tscale = 1.0f/face.mapScale.y;
		float invTx = 1.0f/(face.globalScale*face.textureSize.x);
		float invTy = 1.0f/(face.globalScale*face.textureSize.y);

		// Get the texture offset.
		float soffset = face.mapOffset.x;
		float toffset = face.mapOffset.y;

		size_t numvertices = face.vertices.size();
		for(size_t i = 0; i < numvertices; i++)
		{
			const Vector3 &vertex = face.vertices[i];

			// Calculate the s and t coordinates.
			float s = axis.u.Dot(vertex)*sscale + soffset;
			float t = axis.v.Dot(vertex)*tscale + toffset;
			s *= invTx;
			t *= invTy;

			// Rotate the texture coordinate.
			float olds = s;
			s = cosv*s + sinv*t;
			t = cosv*t - sinv*olds;

			// Store the coordinates.
			face.texcoords.push_back(Vector2(s, t));
		}
	}

	// Generate tangents and binormals.
	void GenerateTBN(BrushFace &face)
	{
		// Basis generation from: Lengyel, Eric.
		// “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”.
		// Terathon Software 3D Graphics Library, 2001.
		// http://www.terathon.com/code/tangent.html

		size_t numvertices = face.vertices.size();
		face.tangents.resize(numvertices);
		face.bitangents.resize(numvertices);

		// Initialize tangents and bitangents.
		for(size_t i = 0; i < numvertices; i++)
		{
			face.tangents[i] = Vector3(0.0f);
			face.bitangents[i] = Vector3(0.0f);
		}

		size_t numtriangles = face.indices.size()/3;
		for(size_t i = 0; i < numtriangles; i++)
		{
			size_t i1 = face.indices[i*3];
			size_t i2 = face.indices[i*3+1];
			size_t i3 = face.indices[i*3+2];

			const Vector3 &v1 = face.vertices[i1];
			const Vector3 &v2 = face.vertices[i2];
			const Vector3 &v3 = face.vertices[i3];

			const Vector2 &c1 = face.texcoords[i1];
			const Vector2 &c2 = face.texcoords[i2];
			const Vector2 &c3 = face.texcoords[i3];

			float x1 = v2.x - v1.x;
			float x2 = v3.x - v1.x;
			float y1 = v2.y - v1.y;
			float y2 = v3.y - v1.y;
			float z1 = v2.z - v1.z;
			float z2 = v3.z - v1.z;

			float s1 = c2.x - c1.x;
			float s2 = c3.x - c1.x;
			float t1 = c2.y - c1.y;
			float t2 = c3.y - c1.y;

			float r = 1.0f / (s1 * t2 - s2 * t1);
			Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
			                (t2 * z1 - t1 * z2) * r);
			Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
			                (s1 * z2 - s2 * z1) * r);

			face.tangents[i1] += sdir;
			face.tangents[i2] += sdir;
			face.tangents[i3] += sdir;

			face.bitangents[i1] += tdir;
			face.bitangents[i2] += tdir;
			face.bitangents[i3] += tdir;

		}

		const Vector3 &n = face.plane.GetNormal();
		for(size_t i = 0; i < numvertices; i++)
		{
			Vector3 t = face.tangents[i];

			// Gram-Schmidt orthogonalize.
			face.tangents[i] = t - n.Dot(t)*n;

			// Calculate handness.
			float w = n.Cross(t).Dot(face.bitangents[i]) < 0.0f ? - 1.0f : 1.0f;

			// Calculate the bitangent.
			face.bitangents[i] = w*n.Cross(t);
		}
	}

	// Generate triangle indices.
	void TriangulateFace(BrushFace &face)
	{
		size_t numvertices = face.vertices.size();
		for(size_t i = 2; i < numvertices; i++)
		{
			face.indices.push_back(0);
			face.indices.push_back(i-1);
			face.indices.push_back(i);
		}
	}
}; // namespace Xern
